package com.systemic.websocket;

import com.systemic.dto.LoginUserDto;
import com.systemic.dto.MessageDto;

import com.systemic.utils.GsonUtils;
import com.systemic.utils.UidExtractorUtils;

import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;
import org.springframework.web.socket.adapter.standard.StandardWebSocketSession;


import java.io.IOException;
import java.net.URI;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;


/**
 * @ClassName: ChatWebSocketHandler
 * @Description: WebSocket处理器  私信模块
 * @Author:
 * @CreateTime: 2024-01-01 15:44
 * @Version:
 **/

@Component
public class ChatWebSocketHandler implements WebSocketHandler {


    //在线用户的 SOCKET session(存储了所有的通信通道)
    public static final Map<String, WebSocketSession> USER_SESSION_MAP;

    private static String uid;

    //存储所有的在线用户
    static {
        USER_SESSION_MAP = new HashMap<String, WebSocketSession>();
    }

    /**
     * WebSocket建立好连接之后的处理函数--连接建立后的准备工作
     * @param webSocketSession
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
        // 获取 WebSocketSession 的 URI
        URI uri = ((StandardWebSocketSession) webSocketSession).getUri();
        assert uri != null;
        uid = UidExtractorUtils.extractUid(uri);
        USER_SESSION_MAP.put(uid, webSocketSession);
        LoginUserDto userDto = new LoginUserDto();
        //获取所有在线的WebSocketSession对象集合
        Set<Entry<String, WebSocketSession>> entrySet = USER_SESSION_MAP.entrySet();
        //将最新的所有的在线人列表放入消息对象的list集合中，用于页面显示
        for (Entry<String, WebSocketSession> entry : entrySet) {
            userDto.getUserList().add((LoginUserDto) entry.getValue().getAttributes().get(uid));
        }
        System.out.println("建立了连接");
    }


    /**
     * 客户端发送服务器的消息时的处理函数，在这里收到消息之后可以分发消息
     * 处理消息：当一个新的WebSocket到达的时候，会被调用（在客户端通过Websocket API发送的消息会经过这里，然后进行相应的处理）
     * @param webSocketSession
     * @param message
     * @throws Exception
     */
    @Override
    public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> message) throws Exception {
        // TODO: 在这里拿到客户端发送的数据
        //如果消息没有任何内容，则直接返回
        if(message.getPayloadLength()==0)return;
        //反序列化服务端收到的json消息
        MessageDto msg = GsonUtils.fromJson(message.getPayload().toString(), MessageDto.class);
        msg.setSendTime(new Date());    // 获取当前时间存储到 sendTime里
        System.out.println("用户"+ msg.getSenderId() +"向用户"+ msg.getReceiverId() +"发送了消息");
        // 可以调用service或mapper存储消息到数据库
        if(msg.getReceiverId()==null){
            throw new RuntimeException("消息为空");
        } else {
            // 调用私信方法
            sendMessageToUser(msg.getReceiverId(), new TextMessage(GsonUtils.toJson(msg)));
        }
    }


    /**
     * 私信方法：给某个人发消息
     * @param id
     * @param message
     * @throws IOException
     */
    private void sendMessageToUser(String id, TextMessage message) throws IOException {
        //获取到要接收消息的用户的session
        WebSocketSession webSocketSession = USER_SESSION_MAP.get(id);
        if (webSocketSession != null && webSocketSession.isOpen()) {
            //发送消息
            webSocketSession.sendMessage(message);
        }
    }

    /**
     * 团体方法：群发消息
     * @param message
     */
    private void sendMessageToAll(final TextMessage message){
        //对用户发送的消息内容进行转义

        //获取到所有在线用户的SocketSession对象
        Set<Map.Entry<String, WebSocketSession>> entrySet = USER_SESSION_MAP.entrySet();
        for (Map.Entry<String, WebSocketSession> entry : entrySet) {
            //某用户的WebSocketSession
            final WebSocketSession webSocketSession = entry.getValue();
            //判断连接是否仍然打开的
            if(webSocketSession.isOpen()){
                //开启多线程发送消息（效率高）
                new Thread(new Runnable() {
                    public void run() {
                        try {
                            if (webSocketSession.isOpen()) {
                                webSocketSession.sendMessage(message);
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                }).start();

            }
        }
    }

    /**
     * 消息传输过程中出现的异常处理函数
     * 处理传输错误：处理由底层WebSocket消息传输过程中发生的异常
     * @param webSocketSession
     * @param throwable
     * @throws Exception
     */
    @Override
    public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
        System.out.println("出现异常");
    }


    /**
     * websocket链接关闭的回调
     * 连接关闭后：一般是回收资源等
     * @param webSocketSession
     * @param closeStatus
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
        // 记录日志，准备关闭连接
        System.out.println("Websocket正常断开:" + webSocketSession.getId() + "已经关闭");
        URI uri = ((StandardWebSocketSession) webSocketSession).getUri();

        assert uri != null;
        uid = UidExtractorUtils.extractUid(uri);
        // 从WebSocket会话中获取用户的信息，如果登录用户为空，则提前结束方法
        LoginUserDto loginUser=(LoginUserDto)webSocketSession.getAttributes().get(uid);
        if (loginUser == null) {
            return;
        }
        LoginUserDto userDto = new LoginUserDto();
        Set<Entry<String, WebSocketSession>> entrySet = USER_SESSION_MAP.entrySet();

        //并查找出在线用户的WebSocketSession（会话），将其移除
        for (Entry<String, WebSocketSession> entry : entrySet) {
            if (entry.getKey().equals(uid)) {
                // 清除在线会话
                USER_SESSION_MAP.remove(entry.getKey());
                // 记录日志：
                System.out.println("用户" + entry.getKey() + "的Socket会话已经移除");
                break;
            }
        }

        // 将在线会话中的用户信息添加到userDto对象中
        for (Entry<String, WebSocketSession> entry : entrySet) {
            userDto.getUserList().add((LoginUserDto)entry.getValue().getAttributes().get(uid));
        }


    }

    /**
     * 是否支持处理拆分消息，true返回拆分消息（一般设置为false）
     * @return
     */
    @Override
    public boolean supportsPartialMessages() {
        return false;
    }
}
